#ifndef __THREAD_H
#define __THREAD_H
#include "matrix.h"
#include "dlist.h"
#include "timer.h"
#include "general.h"
#include "mxsetup.h"

/* default definition */
#define MX_DEFAULT_STACK_SIZE (DEFAULT_STACK_SIZE)      // default stack size of thread
#define MX_DEFAULT_PRIORITY (MX_PRIORITY_MIN / 2)       // default priority of thread
#define MX_DEFAULT_TICK MX_TIMER_MS(DEFAULT_TIME_SLICE) // default time slice (20ms)
#define MX_PRIORITY_MAX PRIORITY_MAX                    // the highest priority of thread
#define MX_PRIORITY_MIN PRIORITY_MIN                    // the lowest priority of thread

#define MX_THREAD_BCOUNT_INITIAL ((mx_usize_t)-1)       // the intial value for bcount

/* get the thread descritor by attribute */
#define mx_thread_container(addr, attribute) container(addr, mx_thread_t, attribute)
/* get the next thread */
#define mx_thread_next(thread) container(thread->list.next, mx_thread_t, list)
/* get the previous thread */
#define mx_thread_prev(thread) container(thread->list.prev, mx_thread_t, list)

/**
 * register a thread without the descriptor
 * @prototype: mx_thread_pt mx_thread_register (mx_string_pt, mx_uint8_t, mx_usize_t, mx_usize_t, mx_thread_entry_t, mx_void_pt);
 * @return: the descriptor of thread, never be mx_null
 * @note: it's a package for `mx_thread_mount`, but different from `mx_thread_create`
 */
#define mx_thread_register(name, priority, tick, stack_size, entry, param) ({   \
    static mx_thread_t _i_thread;    \
    static mx_align_cpu mx_uint8_t _i_stack_buf[stack_size];   \
    mx_thread_mount(&_i_thread, name, priority, tick, _i_stack_buf, stack_size, entry, param);  \
    &_i_thread;  \
})

/**
 * thread status definition
 */
typedef enum
{
    MX_THREAD_STATUS_INIT = 0,      // thread initialization status
    MX_THREAD_STATUS_READY,         // thread ready status
    MX_THREAD_STATUS_BLOCK,         // thread block status
    MX_THREAD_STATUS_RUNING,        // thread runing status
    MX_THREAD_STATUS_DEAD           // thread dead status
} mx_thread_status_t;

/**
 * thread entry point function definition
 * @param param: the argument of thread
 * @return: void
 */
typedef mx_int32_t (*mx_thread_entry_t) (mx_void_pt param);

/**
 * the descriptor of thread definition
 */
typedef struct
{
    /* base information of thread */
    char* name;
    mx_uint8_t status;
    mx_void_pt data;
    /* entry point of thread */
    mx_void_pt param;           // parameter of thread
    mx_thread_entry_t entry;    // entry point of thread
    /* context of thread */
    mx_void_pt stack;           // stack address (stack bottom)
    mx_usize_t size;            // the size of stack
    /* schedule correlation */
    mx_uint8_t priority;        // schedule priority
    mx_timer_t timer;           // thread timer
    mx_usize_t count;           // time slice count
    mx_usize_t bcount;          // the count of timer when be break, e.g. resume, `(mx_usize_t)-1` is initial value
    mx_dlist_t list;            // linked list
} mx_thread_t, *mx_thread_pt;

/**
 * boot the specified thread
 * @param thread: the descriptor of thread
 * @return: void
 */
typedef void (*mx_thread_boot_t) (mx_thread_pt thread);

/* do task schedule, it's invoked out of interrupt */
void mx_thread_schedule (void);
/* mark that need call the `mx_thread_schedule_interrupt` before exit the interrupt */
void mx_thread_schedule_interrupt_need (void);
/* mount a thread to the thread queue */
void mx_thread_mount (mx_thread_pt thread, char* name, mx_uint8_t priority, mx_usize_t tick,
                      mx_void_pt stack, mx_usize_t size, mx_thread_entry_t entry, mx_void_pt param);
/* start up a specified thread, this thread will run immediately if it priority higher than active thread */
void mx_thread_start (mx_thread_pt thread);
/* make current thread to sleep */
void mx_thread_sleep (mx_usize_t ms);
/* make the specified thread to BLOCK, which status is RUNING or READY or BLOKC */
void mx_thread_suspend (mx_thread_pt pthread, mx_dlist_pt tls, mx_usize_t tick);
/* make the specified thread from BLOCK to READY */
void mx_thread_resume (mx_thread_pt pthread);
/* make all of thread's status which in the specified list from BLOCK to READY */
void mx_thread_resumeall (mx_dlist_pt list);
/* kill a thread no matter what status it is in, change status to DEAD */
void mx_thread_kill (mx_thread_pt pthread);
/* exit the current thread */
void mx_thread_exit (void);
/* voluntarily give up the cpu slice */
void mx_thread_concede (void);

/* get the break count (bcount) of thread and reset it */
mx_usize_t mx_thread_bcountc (mx_thread_pt pthread);

#endif
